/******************************************************************************* * Copyright (c) 2003, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ant.tests.ui.testplugin; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import junit.framework.TestCase; import org.eclipse.ant.internal.ui.AntUIPlugin; import org.eclipse.ant.internal.ui.IAntUIPreferenceConstants; import org.eclipse.ant.internal.ui.model.AntModel; import org.eclipse.ant.tests.ui.debug.TestAgainException; import org.eclipse.ant.tests.ui.editor.support.TestLocationProvider; import org.eclipse.ant.tests.ui.editor.support.TestProblemRequestor; import org.eclipse.core.externaltools.internal.IExternalToolConstants; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IProcess; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.BadPositionCategoryException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentPartitioner; import org.eclipse.jface.text.ITypedRegion; import org.eclipse.jface.text.Position; import org.eclipse.swt.graphics.Color; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.console.IHyperlink; import org.eclipse.ui.internal.console.ConsoleHyperlinkPosition; import org.eclipse.ui.internal.console.IOConsolePartition; import org.eclipse.ui.intro.IIntroManager; import org.eclipse.ui.intro.IIntroPart; import org.eclipse.ui.progress.UIJob; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; /** * Abstract Ant UI test class */ public abstract class AbstractAntUITest extends TestCase { public static String ANT_EDITOR_ID = "org.eclipse.ant.ui.internal.editor.AntEditor"; //$NON-NLS-1$ private boolean welcomeClosed = false; private IDocument currentDocument; /** * Constructor * * @param name */ public AbstractAntUITest(String name) { super(name); } /** * Returns the {@link IFile} for the given build file name * * @param buildFileName * @return the associated {@link IFile} for the given build file name */ protected IFile getIFile(String buildFileName) { return getProject().getFolder("buildfiles").getFile(buildFileName); //$NON-NLS-1$ } /** * Returns the {@link File} for the given build file name * * @param buildFileName * @return the {@link File} for the given build file name */ protected File getBuildFile(String buildFileName) { IFile file = getIFile(buildFileName); assertTrue("Could not find build file named: " + buildFileName, file.exists()); //$NON-NLS-1$ return file.getLocation().toFile(); } /** * When a test throws the 'try again' exception, try it again. * * @see junit.framework.TestCase#runBare() */ @Override public void runBare() throws Throwable { boolean tryAgain = true; int attempts = 0; while (tryAgain) { try { attempts++; super.runBare(); tryAgain = false; } catch (TestAgainException e) { System.out.println("Test failed attempt " + attempts + ". Re-testing: " + this.getName()); //$NON-NLS-1$ //$NON-NLS-2$ e.printStackTrace(); if (attempts > 5) { tryAgain = false; } } } } /* * (non-Javadoc) * * @see junit.framework.TestCase#setUp() */ @Override protected void setUp() throws Exception { super.setUp(); assertProject(); assertWelcomeScreenClosed(); } /** * Ensure the welcome screen is closed because in 4.x the debug perspective opens a giant fast-view causing issues * * @throws Exception * @since 3.8 */ void assertWelcomeScreenClosed() throws Exception { if (!welcomeClosed && PlatformUI.isWorkbenchRunning()) { final IWorkbench wb = PlatformUI.getWorkbench(); if (wb != null) { UIJob job = new UIJob("close welcome screen for Ant test suite") { //$NON-NLS-1$ @Override public IStatus runInUIThread(IProgressMonitor monitor) { IWorkbenchWindow window = wb.getActiveWorkbenchWindow(); if (window != null) { IIntroManager im = wb.getIntroManager(); IIntroPart intro = im.getIntro(); if (intro != null) { welcomeClosed = im.closeIntro(intro); } } return Status.OK_STATUS; } }; job.setPriority(Job.INTERACTIVE); job.setSystem(true); job.schedule(); } } } /** * Asserts that the testing project has been setup in the test workspace * * @throws Exception * * @since 3.5 */ public static void assertProject() throws Exception { IProject pro = ResourcesPlugin.getWorkspace().getRoot().getProject(ProjectHelper.PROJECT_NAME); if (!pro.exists()) { // create project and import build files and support files IProject project = ProjectHelper.createProject(ProjectHelper.PROJECT_NAME); IFolder folder = ProjectHelper.addFolder(project, "buildfiles"); //$NON-NLS-1$ ProjectHelper.addFolder(project, "launchConfigurations"); //$NON-NLS-1$ File root = AntUITestPlugin.getDefault().getFileInPlugin(ProjectHelper.TEST_BUILDFILES_DIR); ProjectHelper.importFilesFromDirectory(root, folder.getFullPath(), null); ProjectHelper.createLaunchConfigurationForBoth("echoing"); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForBoth("102282"); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForBoth("74840"); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForBoth("failingTarget"); //$NON-NLS-1$ ProjectHelper.createLaunchConfiguration("build"); //$NON-NLS-1$ ProjectHelper.createLaunchConfiguration("bad"); //$NON-NLS-1$ ProjectHelper.createLaunchConfiguration("importRequiringUserProp"); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForSeparateVM("echoPropertiesSepVM", "echoProperties"); //$NON-NLS-1$ //$NON-NLS-2$ ProjectHelper.createLaunchConfigurationForSeparateVM("extensionPointSepVM", null); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForSeparateVM("extensionPointTaskSepVM", null); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForSeparateVM("extensionPointTypeSepVM", null); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForSeparateVM("input", null); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForSeparateVM("environmentVar", null); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForBoth("breakpoints"); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForBoth("debugAntCall"); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForBoth("96022"); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForBoth("macrodef"); //$NON-NLS-1$ ProjectHelper.createLaunchConfigurationForBoth("85769"); //$NON-NLS-1$ ProjectHelper.createLaunchConfiguration("big", ProjectHelper.PROJECT_NAME + "/buildfiles/performance/build.xml"); //$NON-NLS-1$ //$NON-NLS-2$ // do not show the Ant build failed error dialog AntUIPlugin.getDefault().getPreferenceStore().setValue(IAntUIPreferenceConstants.ANT_ERROR_DIALOG, false); } } /** * Returns the 'AntUITests' project. * * @return the test project */ protected static IProject getProject() { return ResourcesPlugin.getWorkspace().getRoot().getProject(ProjectHelper.PROJECT_NAME); } /** * Returns the underlying {@link IDocument} for the given file name * * @param fileName * @return the underlying {@link IDocument} for the given file name */ protected IDocument getDocument(String fileName) { File file = getBuildFile(fileName); InputStream in; try { in = new FileInputStream(file); } catch (FileNotFoundException e) { return null; } String initialContent = getStreamContentAsString(in); return new Document(initialContent); } /** * Returns the contents of the given {@link InputStream} as a {@link String} * * @param inputStream * @return the {@link InputStream} as a {@link String} */ protected String getStreamContentAsString(InputStream inputStream) { InputStreamReader reader; try { reader = new InputStreamReader(inputStream, ResourcesPlugin.getEncoding()); } catch (UnsupportedEncodingException e) { AntUIPlugin.log(e); return ""; //$NON-NLS-1$ } BufferedReader tempBufferedReader = new BufferedReader(reader); return getReaderContentAsString(tempBufferedReader); } /** * Returns the contents of the given {@link BufferedReader} as a {@link String} * * @param bufferedReader * @return the contents of the given {@link BufferedReader} as a {@link String} */ protected String getReaderContentAsStringNew(BufferedReader bufferedReader) { StringBuffer result = new StringBuffer(); try { char[] readBuffer = new char[2048]; int n = bufferedReader.read(readBuffer); while (n > 0) { result.append(readBuffer, 0, n); n = bufferedReader.read(readBuffer); } } catch (IOException e) { AntUIPlugin.log(e); return null; } return result.toString(); } /** * Returns the contents of the given {@link BufferedReader} as a {@link String} * * @param bufferedReader * @return the contents of the given {@link BufferedReader} as a {@link String} */ protected String getReaderContentAsString(BufferedReader bufferedReader) { StringBuffer result = new StringBuffer(); try { String line = bufferedReader.readLine(); while (line != null) { if (result.length() != 0) { result.append(System.getProperty("line.separator")); //$NON-NLS-1$ } result.append(line); line = bufferedReader.readLine(); } } catch (IOException e) { AntUIPlugin.log(e); return null; } return result.toString(); } /** * Returns the {@link AntModel} for the given file name * * @param fileName * @return the {@link AntModel} for the given file name */ protected AntModel getAntModel(String fileName) { currentDocument = getDocument(fileName); AntModel model = new AntModel(currentDocument, new TestProblemRequestor(), new TestLocationProvider(getBuildFile(fileName))); model.reconcile(); return model; } /** * @return the current {@link IDocument} context */ public IDocument getCurrentDocument() { return currentDocument; } /** * Allows the current {@link IDocument} context to be set. This method accepts <code>null</code> * * @param currentDocument */ public void setCurrentDocument(IDocument currentDocument) { this.currentDocument = currentDocument; } /** * Launches the Ant build with the build file name (no extension). * * @param buildFileName * the ant build file name */ protected void launch(String buildFileName) throws CoreException { ILaunchConfiguration config = getLaunchConfiguration(buildFileName); assertNotNull("Could not locate launch configuration for " + buildFileName, config); //$NON-NLS-1$ launchAndTerminate(config, 20000); } /** * Launches the Ant build with the build file name (no extension). * * @param buildFileName * the build file * @param arguments * the ant arguments */ protected void launch(String buildFileName, String arguments) throws CoreException { ILaunchConfiguration config = getLaunchConfiguration(buildFileName); assertNotNull("Could not locate launch configuration for " + buildFileName, config); //$NON-NLS-1$ ILaunchConfigurationWorkingCopy copy = config.getWorkingCopy(); copy.setAttribute(IExternalToolConstants.ATTR_TOOL_ARGUMENTS, arguments); launchAndTerminate(copy, 20000); } /** * Launches the Ant build in debug output mode with the build file name (no extension). * * @param mainTypeName * the program to launch * @return thread in which the first suspend event occurred */ protected void launchWithDebug(String buildFileName) throws CoreException { ILaunchConfiguration config = getLaunchConfiguration(buildFileName); assertNotNull("Could not locate launch configuration for " + buildFileName, config); //$NON-NLS-1$ ILaunchConfigurationWorkingCopy copy = config.getWorkingCopy(); copy.setAttribute(IExternalToolConstants.ATTR_TOOL_ARGUMENTS, "-debug"); //$NON-NLS-1$ launchAndTerminate(copy, 10000); } /** * Returns the launch configuration for the given build file * * @param buildFileName * build file to launch * @see ProjectCreationDecorator */ protected ILaunchConfiguration getLaunchConfiguration(String buildFileName) { IFile file = getJavaProject().getProject().getFolder("launchConfigurations").getFile(buildFileName + ".launch"); //$NON-NLS-1$ //$NON-NLS-2$ ILaunchConfiguration config = getLaunchManager().getLaunchConfiguration(file); assertTrue("Could not find launch configuration for " + buildFileName, config.exists()); //$NON-NLS-1$ return config; } /** * Returns the content of the specified file as <code>String</code>. */ protected String getFileContentAsString(File aFile) throws FileNotFoundException { InputStream stream = new FileInputStream(aFile); InputStreamReader reader = new InputStreamReader(stream); BufferedReader bufferedReader = new BufferedReader(reader); return getReaderContentAsString(bufferedReader); } /** * @return a new SAX parser instrance */ protected SAXParser getSAXParser() { SAXParser parser = null; try { parser = SAXParserFactory.newInstance().newSAXParser(); } catch (ParserConfigurationException e) { AntUIPlugin.log(e); } catch (SAXException e) { AntUIPlugin.log(e); } return parser; } /** * Parses the given input stream with the given parser using the given handler * * @param stream * @param parser * @param handler * @param editedFile * @throws IOException * @throws SAXException */ protected void parse(InputStream stream, SAXParser parser, DefaultHandler handler, File editedFile) throws SAXException, IOException { InputSource inputSource = new InputSource(stream); if (editedFile != null) { // needed for resolving relative external entities inputSource.setSystemId(editedFile.getAbsolutePath()); } parser.parse(inputSource, handler); } /** * Returns the launch manager * * @return launch manager */ public static ILaunchManager getLaunchManager() { return DebugPlugin.getDefault().getLaunchManager(); } /** * Returns the 'AntUITests' project. * * @return the test project */ public static IJavaProject getJavaProject() { return JavaCore.create(getProject()); } /** * Launches the given configuration and waits for the terminated event or the length of the given timeout, whichever comes first * * @param config * @param timeout * @throws CoreException */ protected void launchAndTerminate(ILaunchConfiguration config, int timeout) throws CoreException { DebugEventWaiter waiter = new DebugElementKindEventWaiter(DebugEvent.TERMINATE, IProcess.class); waiter.setTimeout(timeout); Object terminatee = launchAndWait(config, waiter); assertTrue("terminatee is not an IProcess", terminatee instanceof IProcess); //$NON-NLS-1$ IProcess process = (IProcess) terminatee; boolean terminated = process.isTerminated(); assertTrue("process is not terminated", terminated); //$NON-NLS-1$ } /** * Launches the given configuration and waits for an event. Returns the source of the event. If the event is not received, the launch is * terminated and an exception is thrown. * * @param configuration * the configuration to launch * @param waiter * the event waiter to use * @return Object the source of the event * @exception Exception * if the event is never received. */ protected Object launchAndWait(ILaunchConfiguration configuration, DebugEventWaiter waiter) throws CoreException { ILaunch launch = configuration.launch(ILaunchManager.RUN_MODE, null); Object suspendee = waiter.waitForEvent(); if (suspendee == null) { try { launch.terminate(); } catch (CoreException e) { e.printStackTrace(); } throw new TestAgainException("Retest - Program did not suspend launching: " + configuration.getName()); //$NON-NLS-1$ } boolean terminated = launch.isTerminated(); assertTrue("launch did not terminate", terminated); //$NON-NLS-1$ if (terminated && !ConsoleLineTracker.isClosed()) { ConsoleLineTracker.waitForConsole(); } assertTrue("Console is not closed", ConsoleLineTracker.isClosed()); //$NON-NLS-1$ return suspendee; } /** * Returns the {@link IHyperlink} at the given offset on the given document, or <code>null</code> if there is no {@link IHyperlink} at that offset * on the document. * * @param offset * @param doc * @return the {@link IHyperlink} at the given offset on the given document or <code>null</code> */ protected IHyperlink getHyperlink(int offset, IDocument doc) { if (offset >= 0 && doc != null) { Position[] positions = null; try { positions = doc.getPositions(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY); } catch (BadPositionCategoryException ex) { // no links have been added return null; } for (int i = 0; i < positions.length; i++) { Position position = positions[i]; if (offset >= position.getOffset() && offset <= (position.getOffset() + position.getLength())) { return ((ConsoleHyperlinkPosition) position).getHyperLink(); } } } return null; } /** * Returns the {@link Color} at the given offset on the given document, or <code>null</code> if there is no {@link Color} at that offset on the * document. * * @param offset * @param doc * @return the {@link Color} at the given offset on the given document or <code>null</code> */ protected Color getColorAtOffset(int offset, IDocument document) throws BadLocationException { if (document != null) { IDocumentPartitioner partitioner = document.getDocumentPartitioner(); if (partitioner != null) { ITypedRegion[] regions = partitioner.computePartitioning(offset, document.getLineInformationOfOffset(offset).getLength()); if (regions.length > 0) { IOConsolePartition partition = (IOConsolePartition) regions[0]; return partition.getColor(); } } } return null; } /** * This is to help in increasing the test coverage by enabling access to fields and execution of methods irrespective of their Java language * access permissions. * * More accessor methods can be added to this on a need basis */ protected static abstract class TypeProxy { Object master = null; protected TypeProxy(Object obj) { master = obj; } /** * Gets the method with the given method name and argument types. * * @param methodName * the method name * @param types * the argument types * @return the method */ protected Method get(String methodName, Class<?>[] types) { Method method = null; try { method = master.getClass().getDeclaredMethod(methodName, types); } catch (SecurityException e) { fail(); } catch (NoSuchMethodException ex) { fail(); } Assert.isNotNull(method); method.setAccessible(true); return method; } /** * Invokes the given method with the given arguments. * * @param method * the given method * @param arguments * the method arguments * @return the method return value */ protected Object invoke(Method method, Object[] arguments) { try { return method.invoke(master, arguments); } catch (IllegalArgumentException e) { fail(); } catch (InvocationTargetException e) { fail(); } catch (IllegalAccessException e) { fail(); } return null; } } }